#ifndef __CRYLANMATCHMAKING_H__
#define __CRYLANMATCHMAKING_H__

#if _MSC_VER > 1000
#pragma once
#endif

#include "StdAfx.h"
#include "Lobby/CryMatchMaking.h"
#include "Lobby/CrySharedLobbyPacket.h"


#define MAX_LAN_SEARCH_SERVERS			128
#define MAX_LAN_SEARCHES						2

class CrySharedLobbyPacket;

// If the size and/or layout of SCryLANSessionID changes, its GetSizeInPacket,
// WriteToPacket, and ReadFromPacket must be changed to reflect this. Most
// importantly, these functions must not include size of SCryLANSessionID's vtab
// pointer, serialize the vtab pointer into a packet, or deserialize the vtab
// pointer out of a packet.

struct SCryLANSessionID : public SCrySharedSessionID
{
	CrySessionHandle	m_h;
	uint32						m_ip; 
	uint16						m_port;

	virtual bool operator == (const SCrySessionID &other)
	{
		return (m_h == ((const SCryLANSessionID&)other).m_h) &&
			(m_ip == ((const SCryLANSessionID&)other).m_ip) &&
			(m_port == ((const SCryLANSessionID&)other).m_port);
	};
	virtual bool operator < (const SCrySessionID &other)
	{
		return ((m_h < ((const SCryLANSessionID&)other).m_h) ||
						((m_h == ((const SCryLANSessionID&)other).m_h) && (m_ip < ((const SCryLANSessionID&)other).m_ip)) ||
						((m_h == ((const SCryLANSessionID&)other).m_h) && (m_ip == ((const SCryLANSessionID&)other).m_ip) && (m_port < ((const SCryLANSessionID&)other).m_port)));
	};
	SCryLANSessionID&  operator=(const SCryLANSessionID &other)
	{
		m_h = other.m_h;
		m_ip = other.m_ip; 
		m_port = other.m_port;
		return *this;
	}

	static uint32 GetSizeInPacket()
	{
		return sizeof ( CrySessionHandle ) + sizeof ( uint32 ) + sizeof ( uint16 );
	};

	void WriteToPacket( CCryLobbyPacket* pPacket ) const
	{
		pPacket->WriteUINT32( m_h );
		pPacket->WriteUINT32( m_ip );
		pPacket->WriteUINT16( m_port );
	};

	void ReadFromPacket( CCryLobbyPacket* pPacket )
	{
		m_h = pPacket->ReadUINT32();
		m_ip = pPacket->ReadUINT32();
		m_port = pPacket->ReadUINT16();
	};
	bool IsFromInvite() const
	{
		return true;
	}

	void AsCStr( char* pOutString, int inBufferSize ) const
	{
		snprintf( pOutString, inBufferSize, "%d :%d", m_ip, m_port );
		pOutString[inBufferSize-1]=0;
	}
};

class CCryLANMatchMaking : public CCryMatchMaking
{
public:
													CCryLANMatchMaking(CCryLobby* lobby, CCryLobbyService* service, ECryLobbyService serviceType);

					ECryLobbyError	Initialise();
					void						Tick(CTimeValue tv);

					void						OnPacket(const TNetAddress& addr, CCryLobbyPacket* pPacket);

	virtual ECryLobbyError	SessionRegisterUserData(SCrySessionUserData* data, uint32 numData, CryLobbyTaskID* taskID, CryMatchmakingCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionCreate(uint32* users, int numUsers, uint32 flags, SCrySessionData* data, CryLobbyTaskID* taskID, CryMatchmakingSessionCreateCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionMigrate(CrySessionHandle h,uint32* pUsers, int numUsers, uint32 flags, SCrySessionData* pData, CryLobbyTaskID* pTaskID, CryMatchmakingSessionCreateCallback pCB, void* pCBArg);
	virtual ECryLobbyError	SessionUpdate(CrySessionHandle h, SCrySessionUserData* data, uint32 numData, CryLobbyTaskID* taskID, CryMatchmakingCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionUpdateSlots(CrySessionHandle h, uint32 numPublic, uint32 numPrivate, CryLobbyTaskID* pTaskID, CryMatchmakingCallback pCB, void* pCBArg);
	virtual ECryLobbyError	SessionQuery(CrySessionHandle h, CryLobbyTaskID* pTaskID, CryMatchmakingSessionQueryCallback pCB, void* pCBArg);
	virtual ECryLobbyError	SessionGetUsers(CrySessionHandle h, CryLobbyTaskID* pTaskID, CryMatchmakingSessionGetUsersCallback pCB, void* pCBArg);
	virtual ECryLobbyError	SessionStart(CrySessionHandle h, CryLobbyTaskID* taskID, CryMatchmakingCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionEnd(CrySessionHandle h, CryLobbyTaskID* taskID, CryMatchmakingCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionDelete(CrySessionHandle h, CryLobbyTaskID* taskID, CryMatchmakingCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionSearch(uint32 user, SCrySessionSearchParam* param, CryLobbyTaskID* taskID, CryMatchmakingSessionSearchCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionJoin(uint32* users, int numUsers, uint32 flags, CrySessionID id, CryLobbyTaskID* taskID, CryMatchmakingSessionJoinCallback cb, void* cbArg);
	virtual ECryLobbyError	SessionSetLocalUserData(CrySessionHandle h, CryLobbyTaskID* pTaskID, uint32 user, uint8* pData, uint32 dataSize, CryMatchmakingCallback pCB, void* pCBArg);
	virtual CrySessionID		SessionGetCrySessionIDFromCrySessionHandle(CrySessionHandle h);
	virtual CrySessionID		GetSessionIDFromConsole(void);

	virtual uint32					GetSessionIDSizeInPacket() const;
	virtual ECryLobbyError	WriteSessionIDToPacket(CrySessionID sessionId, CCryLobbyPacket* pPacket) const;
	virtual CrySessionID		ReadSessionIDFromPacket(CCryLobbyPacket* pPacket) const;

	virtual ECryLobbyError	SessionEnsureBestHost(CrySessionHandle gh, CryLobbyTaskID* pTaskID, CryMatchmakingCallback pCB, void* pCBArg);
#if NETWORK_HOST_MIGRATION
	virtual void						HostMigrationInitialise(CrySessionHandle h, EDisconnectionCause cause);
	virtual void						HostMigrationFinalise(CrySessionHandle h);
	virtual ECryLobbyError	HostMigrationServer(SHostMigrationInfo* pInfo);
	virtual bool						GetNewHostAddress(char* address, SHostMigrationInfo* pInfo);
#endif

private:
	enum ETask
	{
		eT_SessionRegisterUserData = eT_MatchMakingPlatformSpecificTask,
		eT_SessionCreate,
		eT_SessionMigrate,
		eT_SessionUpdate,
		eT_SessionUpdateSlots,
		eT_SessionQuery,
		eT_SessionGetUsers,
		eT_SessionStart,
		eT_SessionEnd,
		eT_SessionDelete,
		eT_SessionSearch,
		eT_SessionJoin,
		eT_SessionSetLocalUserData,

		// ^^New task IDs *must* go before host migration tasks^^
		eT_SessionMigrateHostStart	= HOST_MIGRATION_TASK_ID_FLAG,
		eT_SessionMigrateHostServer,
		eT_SessionMigrateHostClient,
		eT_SessionMigrateHostFinish
	};

					ECryLobbyError	StartTask(ETask task, CryMatchMakingTaskID* mmTaskID, CryLobbyTaskID* lTaskID, CrySessionHandle h, void* cb, void* cbArg);
					void						StartTaskRunning(CryMatchMakingTaskID mmTaskID);
					void						StopTaskRunning(CryMatchMakingTaskID mmTaskID);
					void						EndTask(CryMatchMakingTaskID mmTaskID);
					ECryLobbyError	CreateSessionHandle(CrySessionHandle *h, bool host, uint32 createFlags, int numUsers);
					CryMatchMakingConnectionID AddRemoteConnection(CrySessionHandle h, CryLobbyConnectionID connectionID, SCryMatchMakingConnectionUID uid, uint32 ip, uint16 port, uint32 numUsers,const char *name, uint8 userData[CRYLOBBY_USER_DATA_SIZE_IN_BYTES], bool isDedicated);
	virtual void						FreeRemoteConnection(CrySessionHandle h, CryMatchMakingConnectionID id);
	virtual uint64					GetSIDFromSessionHandle(CrySessionHandle h);
					ECryLobbyError	CreateSessionSearchHandle(CrySessionHandle *h);
					ECryLobbyError	SetSessionUserData(CrySessionHandle h, SCrySessionUserData* data, uint32 numData);
					ECryLobbyError	SetSessionData(CrySessionHandle h, SCrySessionData* data);
					size_t					CalculateServerDataSize(CrySessionHandle h);
					void						SendServerData(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						ServerDataToGame(CryMatchMakingTaskID mmTaskID, uint32 ip, uint16 port, TMemHdl params, uint32 length);
					void						ProcessServerData(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					CrySessionHandle	FindSessionFromServerID(CrySessionHandle h);

					void						StartSessionJoin(CryMatchMakingTaskID mmTaskID);
					void						TickSessionJoin(CryMatchMakingTaskID mmTaskID);
					void						ProcessSessionRequestJoin(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						ProcessSessionRequestJoinResult(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						ProcessSessionAddRemoteConnections(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);

					void						StartSessionDelete(CryMatchMakingTaskID mmTaskID);

					void						SendSessionQuery(CTimeValue tv,uint32 index, bool broadcast);
					void						SetLocalUserName(CrySessionHandle h, uint32 localUserIndex);
					virtual const char*	GetConnectionName(CCryMatchMaking::SSession::SRConnection* pConnection, uint32 localUserIndex) const;
					void						StartSessionSetLocalUserData(CryMatchMakingTaskID mmTaskID);
					void						ProcessLocalUserData(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						EndSessionGetUsers(CryMatchMakingTaskID mmTaskID);

#if NETWORK_HOST_MIGRATION
					void						HostMigrationServerNT(CryMatchMakingTaskID mmTaskID);
					ECryLobbyError	HostMigrationClient(CrySessionHandle h, CryMatchMakingTaskID hostTaskID);
					void						TickHostMigrationClientNT(CryMatchMakingTaskID mmTaskID);
					void						ProcessHostMigrationFromServer(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						ProcessHostMigrationFromClient(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
					void						TickHostMigrationFinishNT(CryMatchMakingTaskID mmTaskID);
					void						HostMigrationStartNT(CryMatchMakingTaskID mmTaskID);
					void						TickHostMigrationStartNT(CryMatchMakingTaskID mmTaskID);
					void						ProcessHostMigrationStart(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket);
#endif
					void						SessionUserDataEvent(ECryLobbySystemEvent event, CrySessionHandle h, CryMatchMakingConnectionID id);
					void						InitialUserDataEvent(CrySessionHandle h);

					void						ResetTask(CryMatchMakingTaskID mmTaskID);
					void						DispatchRoomOwnerChangedEvent(TMemHdl mh);

	struct	SRegisteredUserData
	{
		SCrySessionUserData		data[MAX_MATCHMAKING_SESSION_USER_DATA];
		uint32								num;
	}												m_registeredUserData;

	struct	SSession : public CCryMatchMaking::SSession
	{
		virtual const char* GetLocalUserName(uint32 localUserIndex) const;

		SCrySessionUserData		userData[MAX_MATCHMAKING_SESSION_USER_DATA];
		SCrySessionData				data;
		SCryLANSessionID			id;
		uint64								sid;
		uint32								numFilledSlots;

		struct SLConnection : public CCryMatchMaking::SSession::SLConnection
		{
			char								name[CRYLOBBY_USER_NAME_LENGTH];
			uint8								userData[CRYLOBBY_USER_DATA_SIZE_IN_BYTES];
		}											localConnection;

		struct SRConnection : public CCryMatchMaking::SSession::SRConnection
		{
			char								name[CRYLOBBY_USER_NAME_LENGTH];
			uint8								userData[CRYLOBBY_USER_DATA_SIZE_IN_BYTES];
			uint32							ip;
			uint16							port;
#if NETWORK_HOST_MIGRATION
			bool								m_migrated;
#endif
			bool								m_isDedicated;																		// Remote connection is a dedicated server
		}											remoteConnection[MAX_LOBBY_CONNECTIONS];
	};

	struct	SSearch
	{
		SCryLANSessionID			servers[MAX_LAN_SEARCH_SERVERS];
		uint32								numServers;
		bool									used;
	}												m_search[MAX_LAN_SEARCHES];

	struct	STask : public CCryMatchMaking::STask
	{
		uint32								ticks;
	};

	SSession								m_sessions[MAX_MATCHMAKING_SESSIONS];
	STask										m_task[MAX_MATCHMAKING_TASKS];
};

#endif // __CRYMATCHMAKING_H__
